package cn.yangliu.operation.aspect;

import cn.yangliu.comm.BaseComponent;
import cn.yangliu.comm.CommConst;
import cn.yangliu.comm.tools.SpringUtil;
import cn.yangliu.comm.tools.WebUtils;
import cn.yangliu.operation.OperatorHunter;
import cn.yangliu.operation.annotation.Action;
import cn.yangliu.operation.annotation.Module;
import cn.yangliu.operation.annotation.Operate;
import cn.yangliu.operation.entity.Operation;
import cn.yangliu.operation.entity.Operator;
import cn.yangliu.operation.enums.OperateLevelEnum;
import cn.yangliu.operation.service.OperationService;
import cn.yangliu.operation.tools.OperationUtils;
import com.alibaba.fastjson.JSON;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 作者 杨柳
 * 创建时间 2017-08-25 23:45
 */
@Component
@Aspect
public class OperationAspect extends BaseComponent implements Ordered {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)||@annotation(org.springframework.web.bind.annotation.PostMapping)||@annotation(org.springframework.web.bind.annotation.GetMapping)")
    public void advice() {
    }


    //创建操作日志
    private Operation createRecord(ProceedingJoinPoint joinPoint, Operator operator, String params, String remark, String host) {
        Operation operation = null;
        String operatorId, operatorName, moduleName, actionName, level, methodName;
        Module module = joinPoint.getTarget().getClass().getAnnotation(Module.class);
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        methodName = method.getName();
        if (operator != null) {
            operatorId = operator.getOperatorId().toString();
            operatorName = operator.getOperatorName();
        } else {
            operatorId = "0L";
            operatorName = "外部系统";
        }
        //找方法上的的@Operate注解
        Operate operate = method.getDeclaredAnnotation(Operate.class);
        if (operate != null) { //
            actionName = operate.action();
            moduleName = operate.module();
            //操作危险级别
            OperateLevelEnum levelEnum = operate.operateLevel();
            level = levelEnum.getLevel();
            operation = getInstance(operatorId, operatorName, params, host, moduleName, actionName, level, methodName, remark);
        } else if (module != null) {
            Action action = method.getDeclaredAnnotation(Action.class);
            if (action != null) {
                //操作危险级别
                OperateLevelEnum levelEnum = action.operateLevel();
                level = levelEnum.getLevel();
                moduleName = module.value();
                actionName = action.value();
                operation = getInstance(operatorId, operatorName, params, host, moduleName, actionName, level, methodName, remark);
            }
        }
        return operation;
    }

    private Operation getInstance(String operatorId, String operator, String params, String host,
                                  String module, String action, String level, String method, String remark) {
        Operation operation = null;
        try {
            if (OperationUtils.ENTITY_CLASS == null) {
                throw new NullPointerException("没有指定操作日志的实体类,请先指定");
            }
            Object obj = OperationUtils.ENTITY_CLASS.newInstance();
            operation = (Operation) (obj);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        operation.setOperatorId(operatorId);
        operation.setOperator(operator);
        operation.setParams(params);
        operation.setAction(action);
        operation.setServerMethod(method);
        operation.setOperateLevel(level);
        operation.setModule(module);
        operation.setHost(host);
        operation.setOprateTime(LocalDateTime.now());
        operation.setRemark(remark);
        return operation;
    }


    @Around("advice()")
    public Object operateAspect(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        Throwable t = null;
        boolean flag = true;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            flag = false;
            t = e;
        }

        final boolean operate_flag = flag;
        HttpServletRequest request = WebUtils.getRequest();
        Operator operator;
        OperatorHunter hunter = SpringUtil.getBean(OperatorHunter.class);
        operator = hunter.getOperator(request);
//            operator = WebUtils.getAttributeFromSession(CommConst.SESSION_USER);

        String host = WebUtils.getIp();
        String paramsJson = JSON.toJSONString(request.getParameterMap());
        String remark = OperationUtils.getValue();
        CommConst.THREDPOOL.submit(() -> {
            try {
                Operation operation = createRecord(joinPoint, operator, paramsJson, remark, host);
                //日志记录
                if (operation != null) {
                    operation.setOperateStatus(operate_flag);
                    OperationService operationService = SpringUtil.getBean(OperationService.class);
                    operationService.insertOperation(operation);
                }
            } catch (Exception e) {
                logger.info(e.getMessage(), e);
                throw new RuntimeException(e.getMessage(), e);
            }
        });

        if (t != null) {
            throw t;
        }

        return result;
    }

    private int order = Integer.MAX_VALUE;

    @Override
    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}
